Инвариант класса

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

В программировании, в частности объектно-ориентированном программировании, инвариант класса (или инвариант типа) — инвариант, используемый для ограничения объектов класса. Методы класса должны сохранять инвариант.

Во время создания классов устанавливаются их инварианты, которые постоянно поддерживаются между вызовами публичных методов. Временное нарушение классовой инвариантности между частными вызовами метода возможно, хотя и нежелательно.

Инвариант объекта представляет собой конструкцию программирования, состоящую из набора инвариантных свойств. Это гарантирует, что объект всегда будет соответствовать предопределенным условиям, и поэтому методы могут всегда ссылаться на объект без риска сделать неточные презумпции. Определение инвариантов классов может помочь программистам и тестировщикам обнаружить больше ошибок при тестировании программного обеспечения.

Инварианты классов и наследование[править | править код]

Полезный эффект инвариантов класса в объектно-ориентированном программировании увеличивается при наличии наследования. Инварианты класса наследуются, то есть инварианты всех родителей класса относятся к самому классу.[1]

Наследование позволяет классам-потомкам изменять данные реализации родительских классов, поэтому класс потомков может изменять состояние экземпляров таким образом, чтобы сделать их недействительными с точки зрения родительского класса. Обращение к этому типу неверного потомка является одной из причин, по которой объектно-ориентированные разработчики программного обеспечения дают предпочтение инкапсуляции наследованию.[2]

Однако, поскольку инварианты класса наследуются, инвариант класса для любого конкретного класса состоит из любых инвариантных утверждений, закодированных непосредственно в этом классе, совмещено со всеми инвариантными предложениями, унаследованными от родителей этого класса. Это означает, что, хотя классы потомков могут иметь доступ к данным реализации своих родителей, инвариант класса может помешать им манипулировать этими данными любым способом, который создаёт недопустимый экземпляр во время выполнения.

Поддержка языков программирования[править | править код]

Утверждения[править | править код]

Такие языки программирования, как C++ и Java, поддерживают утверждения по умолчанию, что может использоваться для определения инвариантов класса. Общим примером реализации инвариантов в классах является то, что конструктор класса генерирует исключение, если инвариант не выполняется. Поскольку методы сохраняют инварианты, они могут принять справедливость инварианта и не должны явно проверять его.

Родная поддержка[править | править код]

Инвариант класса является существенным компонентом контрактного программирования. Таким образом, языки программирования, обеспечивающие полную поддержку контрактного программирования, такие как Eiffel, Ада и D, также будут обеспечивать полную поддержку инвариантов классов.

Неродная поддержка[править | править код]

В Java существует более мощный инструмент, называемый Java Modeling Language, который обеспечивает более надежный способ определения инвариантов класса.

Примеры[править | править код]

Родная поддержка[править | править код]

D[править | править код]

Язык программирования D имеет встроенную поддержку инвариантов класса, а также другие методы программирования контрактов. Вот пример из официальной документации.

class Date {
  int day;
  int hour;

  invariant() {
    assert(1 <= day && day <= 31);
    assert(0 <= hour && hour < 24);
  }
}

Eiffel[править | править код]

В Eiffel инвариант класса объявляется в конце класса после ключевого слова.

class
	DATE

create
	make

feature {NONE} -- Initialization

	make (a_day: INTEGER; a_hour: INTEGER)
			-- Initialize `Current' with `a_day' and `a_hour'.
		require
			valid_day: 1 <= a_day and a_day <= 31
			valid_hour: 0 <= a_hour and a_hour <= 23
		do
			day := a_day
			hour := a_hour
		ensure
			day_set: day = a_day
			hour_set: hour = a_hour
		end

feature -- Access

	day: INTEGER
		-- Day of month for `Current'

	hour: INTEGER
		-- Hour of day for `Current'

feature -- Element change

	set_day (a_day: INTEGER)
			-- Set `day' to `a_day'
		require
			valid_argument: 1 <= a_day and a_day <= 31
		do
			day := a_day
		ensure
			day_set: day = a_day
		end

	set_hour (a_hour: INTEGER)
			-- Set `hour' to `a_hour'
		require
			valid_argument: 0 <= a_hour and a_hour <= 23
		do
			hour := a_hour
		ensure
			hour_set: hour = a_hour
		end

invariant
	valid_day: 1 <= day and day <= 31
	valid_hour: 0 <= hour and hour <= 23
end

Неродная поддержка[править | править код]

Java[править | править код]

Это пример инварианта класса в языке программирования Java с Java Modeling Language. Инвариант должен иметь значение true после завершения конструктора и при входе, и выходе всех членов публичной функции, которые должны определять предварительное условие и постусловие, чтобы обеспечить инвариант класса.

public class Date {
    int /*@spec_public@*/ day;
    int /*@spec_public@*/ hour;

    /*@invariant 1 <= day && day <= 31; @*/ //class invariant
    /*@invariant 0 <= hour && hour < 24; @*/ //class invariant

    /*@
    @requires 1 <= d && d <= 31;
    @requires 0 <= h && h < 24;
    @*/
    public Date(int d, int h) { // constructor
        day = d;
        hour = h;
    }

    /*@
    @requires 1 <= d && d <= 31;
    @ensures day == d;
    @*/
    public void setDay(int d) {
        day = d;
    }

    /*@
    @requires 0 <= h && h < 24;
    @ensures hour == h;
    @*/
    public void setHour(int h) {
        hour = h;
    }
}

Примечания[править | править код]

  1. Bertrand Meyer. Object-Oriented Software Construction. — 2-е изд. — 1997. — С. 570.
  2. E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. — 1995. — С. 20.